/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package simple;

import algorithm.AlgorithmErrorException;
import algorithm.Encoder;
import algorithm.Genotype;
import algorithm.Phenotype;

/**
 *
 * @author vermaak
 */
public class DoubleArrayBitStringEncoder implements Encoder {

    private int bitsPerValue;
    private int nDoubleValues;
    
    private transient double scaleFactor;

    public DoubleArrayBitStringEncoder(int bitsPerValue, int nDoubleValues) {
        this.bitsPerValue = bitsPerValue;
        this.nDoubleValues = nDoubleValues;
        
        this.scaleFactor = 1 << bitsPerValue;
    }
    
    /** Encode a bitstring to an array of doubles
     * 
     * @param genotype Has to be a bitstring genotype
     * @return
     * @throws algorithm.AlgorithmErrorException
     */
    public Phenotype toPhenoType(Genotype genotype) throws AlgorithmErrorException {
       if (!(genotype instanceof BitStringGenotype)) {
           throw new AlgorithmErrorException("This encoder requires bitstring genotypes.");
       }
       short[] bits = ((BitStringGenotype)genotype).getBits();
       double[] phenoArray = new double[nDoubleValues];
       for (int i=0;i<nDoubleValues;++i) {
           int val = 0;
           for (int j=0;j<bitsPerValue;j++) {
                val += bits[i*bitsPerValue+j] << j;
           }
           phenoArray[i] = (double)val/scaleFactor;
       }
       return new DoubleArrayPhenotype(phenoArray);
    }

    public Genotype toGenoType(Phenotype phenotype) throws AlgorithmErrorException {
        if (!(phenotype instanceof DoubleArrayPhenotype)) {
           throw new AlgorithmErrorException("This encoder requires double array phenotypes.");     
        }
        short[] bits = new short[nDoubleValues*bitsPerValue];
        double[] values = ((DoubleArrayPhenotype)phenotype).getValues();
        assert(values.length == nDoubleValues);
        
        for (int i=0;i<nDoubleValues;++i) {
            assert(values[i] < 1.0);
            // scale it up
            int val = (int)(values[i]*scaleFactor);
            for (int j=0;j<bitsPerValue;++j) {
                bits[i*bitsPerValue +j] = (short)(val >> j);
            }
        }
        return new BitStringGenotype(bits);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final DoubleArrayBitStringEncoder other = (DoubleArrayBitStringEncoder) obj;
        if (this.bitsPerValue != other.bitsPerValue) {
            return false;
        }
        if (this.nDoubleValues != other.nDoubleValues) {
            return false;
        }
        if (this.scaleFactor != other.scaleFactor) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 83 * hash + this.bitsPerValue;
        hash = 83 * hash + this.nDoubleValues;
        hash = 83 * hash + (int) (Double.doubleToLongBits(this.scaleFactor) ^ (Double.doubleToLongBits(this.scaleFactor) >>> 32));
        return hash;
    }
    
    

}
